/***************************************************************************
 * file:        BaseNetwLayer_SRT_2.cc
 *
 * author:      Daniel Willkomm
 *
 * copyright:   (C) 2004 Telecommunication Networks Group (TKN) at
 *              Technische Universitaet Berlin, Germany.
 *
 *              This program is free software; you can redistribute it
 *              and/or modify it under the terms of the GNU General Public
 *              License as published by the Free Software Foundation; either
 *              version 2 of the License, or (at your option) any later
 *              version.
 *              For further information see file COPYING
 *              in the top level directory
 ***************************************************************************
 * part of:     framework implementation developed by tkn
 * description: network layer: general class for the network layer
 *              subclass to create your own network layer
 ***************************************************************************/


#include "BaseNetwLayer_SRT_2.h"
#include "NetwControlInfo.h"
#include "NetwToMacControlInfo.h"
#include "BaseMacLayer.h"
#include "AddressingInterface.h"

#include <cassert>

Define_Module(BaseNetwLayer_SRT_2);

void BaseNetwLayer_SRT_2::initialize(int stage)
{

	int index;
	BaseLayer::initialize(stage);
	numForwardedPackets= 0;
	numberTransmittedPacketsTwoHops = 0;
	numberTransmittedPacketsOneHop = 0;
	numberReceivedOneHop = 0;
	numberTtlZeroReceived = 0;
	numberReceivedTwoHop = 0;

	if (stage==0){
		coreDebug = par("coreDebug").boolValue();
		headerLength= par("headerLength");
		arp = FindModule<ArpInterface*>::findSubModule(findHost());
		maxTtl = par("maxTtl").longValue();
		ip = par("ip").longValue();
		twoHop = par("twoHop").boolValue();
		chainTopol = par("chainTopol").boolValue();
		numHosts = par("numHosts").longValue();
		numHostsInRun = numHosts;

		//boredTime = par("boredTime").doubleValue();
		//numHostsInRun = par("numHostsInRun").longValue();

	}
	else if(stage == 1) {
		// see if there is an addressing module available
		// otherwise use module id as network address
		AddressingInterface* addrScheme = FindModule<AddressingInterface*>
		::findSubModule(findHost());
		if(addrScheme) {
			myNetwAddr = addrScheme->myNetwAddr(this);
		} else {
			myNetwAddr = getId();
		}
		coreEV << " ===================+++++++===" << "\n";
		coreEV << " myNetwAddr " << myNetwAddr << "\n";
		coreEV << " ===================+++++++===" << "\n";
	}

	networkAddressMapGlbl1[ip] = myNetwAddr;
	for (index = 0; index < numHosts; index++)
	{
		oneHopDestTableRT[index] = -99;
		twoHopDestTableRT[index] = -99;
		targetForwardTableRT[index] = -99;
	}

	if (ip == 0)
	{
		oneHopDestTableRT[1] = 1;
		twoHopDestTableRT[1] = 1;
		twoHopDestTableRT[2] = 2;
		targetForwardTableRT[2] = 1;
	}
	else if (ip == numHosts)
	{
		oneHopDestTableRT[numHosts-1] = numHosts-1;
		twoHopDestTableRT[numHosts-1] = numHosts-1;
		twoHopDestTableRT[numHosts-2] = numHosts-2;
		targetForwardTableRT[numHosts-2] = numHosts-1;
	}
	else
	{
		oneHopDestTableRT[ip+1] = ip+1;
		oneHopDestTableRT[ip-1] = ip-1;
		
		twoHopDestTableRT[ip-1] = ip-1;
		twoHopDestTableRT[ip-2] = ip-2;
		twoHopDestTableRT[ip+1] = ip+1;
		twoHopDestTableRT[ip+2] = ip+2;
		
		targetForwardTableRT[ip-2] = ip-1;
		targetForwardTableRT[ip+2] = ip+1;
	}

	if (twoHop == true)
	{
		destinationTableRT = twoHopDestTableRT;   //forwarding table
		coreEV << "Two Hop Destination Table Selected \n";
	}
	else
	{
		destinationTableRT = oneHopDestTableRT;
		coreEV << "One Hop Destination Table Selected \n";
	}
	for (index = 0; index<numHosts; index++)
	{
		coreEV << "ip = " << ip << "\t destinationTable[" << index << "] = " << destinationTableRT[index] << "\t two hop "<< twoHopDestTableRT[index] << "\t one hop " << oneHopDestTableRT[index] << "\n" ;
	}

}

/**
 * Decapsulates the packet from the received Network packet
 **/
cMessage* BaseNetwLayer_SRT_2::decapsMsg(Netw80211Pkt *msg)
{
	cMessage *m = msg->decapsulate();
	m->setControlInfo(new NetwControlInfo(msg->getSrcAddr()));
	// delete the netw packet
	delete msg;
	return m;
}


/**
 * Encapsulates the received ApplPkt into a Netw80211Pkt and set all needed
 * header fields.
 **/
Netw80211Pkt* BaseNetwLayer_SRT_2::encapsMsg(cPacket *appPkt) {
	int macAddr;
	int netwAddr;
	int localCounter = 0;
	int finalNetwAddr;


	coreEV <<"in encaps...\n";

	Netw80211Pkt *pkt = new Netw80211Pkt(appPkt->getName(), appPkt->getKind());
	pkt->setBitLength(headerLength);

	NetwControlInfo* cInfo = dynamic_cast<NetwControlInfo*>(appPkt->removeControlInfo());

	if(cInfo == 0){
		EV << "warning: Application layer did not specifiy a destination L3 address\n"
				<< "\tusing broadcast address instead\n";
		netwAddr = L3BROADCAST;
	} else {
		coreEV <<"CInfo removed, netw addr="<< cInfo->getNetwAddr()<<endl;
		netwAddr = cInfo->getNetwAddr();
		delete cInfo;
	}

	//-----------------------------------------
	// A D D I T I O N - SRT
	//-----------------------------------------
	int sizeRT = 0; // = destinationTableRT.size();
	int target = intrand(numHostsInRun); //intrand(sizeRT); //intrand(numHostsInRun); //(routingTable.size());
	int destinationTableNonZero = 0;
	int index = 0;
	//for(index = 0; index< numHosts; index++)
	//{
	//	ev << "index " << index << "\tdestinationTable[index]" << destinationTable[index] <<"\n";
	//}
	for( index = 0; index<numHosts; index++){
		if(destinationTableRT[index]>=0 )
		{
			sizeRT++;
		}

	}
	if( netwAddr > 0)
	{
		while((destinationTableNonZero ==0) & (index < numHosts))
		{
			if(destinationTableRT[index]>=0)
			{
				if (networkAddressMapGlbl1[destinationTableRT[index]] == netwAddr)
				{
					destinationTableNonZero = networkAddressMapGlbl1[destinationTableRT[index]];
				}
			}
			else
			{
				index++;
			}
		}
		if(destinationTableNonZero >0)
		{
			target = index; // -1;
			ev << "NOT REDIRECTING - NETW ADRESS > 0 \n\n";
		}
		else
		{

			netwAddr = -1;
			ev << "REDIRECTING NETW ADRESS > 0, NETW ADRESS NOT IN DEST TABLE \n";
		}
	}
	//If netwAddr not a legal value or we are broadcasting to all (netwAddr = -1)
	if( netwAddr < 1)
	{
		ev << "NUM HOSTS = " << numHostsInRun << "\n";
		if(destinationTableRT[target] < 0) //(networkAddressMapGlbl1[destinationTableRT[target]] == 0))
		{
			if(sizeRT >0 )
			{
				ev  << "ENTERING WHILE LOOP !!!\n";
				//while( (networkAddressMapGlbl1[destinationTableRT[target]] == 0) & (localCounter <500))
				while( (destinationTableRT[target] < 0) & (localCounter <500))
				{
					//ev << "TARGET = " << target << "\n";
					localCounter++;
					target = intrand(numHosts);
				}
				ev << "FINAL TARGET = " << target << "\t LOCAL COUNTER = " << localCounter << "\n";
			}
			else
			{
				ev << "ERROR - table size == 0 !!!!!!!!!\n";

			}
		}
	}
	if(destinationTableRT[target]>=0)
	{
		finalNetwAddr = networkAddressMapGlbl1[destinationTableRT[target]]; //destinationTable[target]; //it->first;
	}
	else //if(finalNetwAddr == 0)
	{
		ev <<"ERROR - redirecting of packet failed !!!!!!!!!!! \n Packet Broadcasted to all \n\n";
		netwAddr = -1;
		finalNetwAddr = -1;
	}


	if((twoHop == false)|(oneHopDestTableRT[target] == twoHopDestTableRT[target])|(finalNetwAddr == -1))
	{
		netwAddr = finalNetwAddr;
		ev << "DESTINATION = SINGLE HOP DESTINATION \n";
		numberTransmittedPacketsOneHop++;
	}
	else
	{
		if(targetForwardTableRT[target]>=0)//(networkAddressMapGlbl1[targetForwardTableRT[target]]>0)
		{
			numberTransmittedPacketsTwoHops++;
			netwAddr = networkAddressMapGlbl1[targetForwardTableRT[target]]; // Next hop address !!finalNetwAddr; //oneHopDestTable[forwardingTable[target]];
		}
		else
		{
			netwAddr = finalNetwAddr;
			ev << "CONFIG ERROR - INTENDED DEST ADRESS NOT REACHABLE BY 2 HOPS\n";
		}
	}
	if(netwAddr == 0)
	{
		netwAddr = -1;
		ev << "ERROR - DEST ADDRESS SET TO BROADCAST (-1) \n";
	}

	coreEV << "size of table = " << sizeRT << "\t target = "<< target << " \t destinationTable[target] = " <<destinationTableRT[target]<< "\t netwAddr = " << netwAddr <<"\n";

	if(twoHop == false)
	{
		ev << "SINGLE HOP Destination Table !!!!!!! No Forwarding. \n";
	}
	else
	{
		ev << "DOUBLE HOP Destination Table !!!!!!!!! Messages Forwarded Once\n";
	}

	//-----------------------------------------
	//-----------------------------------------

	pkt->setSrcAddr(myNetwAddr);
	pkt->setDestAddr(netwAddr);
	pkt->setFinalDestAddr(finalNetwAddr);
	pkt->setTtl(maxTtl);
	coreEV << " netw "<< myNetwAddr << " sending packet" <<endl;
	if(netwAddr == L3BROADCAST) {
		coreEV << "sendDown: nHop=L3BROADCAST -> message has to be broadcasted"
				<< " -> set destMac=L2BROADCAST\n";
		macAddr = L2BROADCAST;
	}
	else{
		coreEV <<"sendDown: get the MAC address\n";
		macAddr = arp->getMacAddr(netwAddr);
	}
	pkt->setControlInfo(new NetwToMacControlInfo(macAddr));

	//encapsulate the application packet
	pkt->encapsulate(appPkt);
	coreEV <<" pkt encapsulated\n";
	return pkt;
}

/**
 * Redefine this function if you want to process messages from lower
 * layers before they are forwarded to upper layers
 *
 *
 * If you want to forward the message to upper layers please use
 * @ref sendUp which will take care of decapsulation and thelike
 **/
/*
void BaseNetwLayer::handleLowerMsg(cMessage* msg)
{
    Netw80211Pkt *m = static_cast<Netw80211Pkt *>(msg);
    coreEV << " handling packet from " << m->getSrcAddr() << endl;
    sendUp(decapsMsg(m));
}*/
void BaseNetwLayer_SRT_2::handleLowerMsg(cMessage* msg)
{
	Netw80211Pkt *m = static_cast<Netw80211Pkt *>(msg);
	//Netw80211Pkt *pkt = static_cast<Netw80211Pkt *>(msg);

	//---------------------------------
	//int macAddr;
	//int netwAddr;
	//int localCounter = 0;
	int finalDestNetwAddr = m->getFinalDestAddr();
	//int tempAddress =  m->getDestAddr();
	int destNetwAddress =  m->getDestAddr();
	int remainingTtl = m->getTtl();

	int nextHop;
	int forwardTableHasDestAddress = 1;
	int index = 0;

	//int myMacAddress = arp->getMacAddr(myNetwAddr);


	coreEV << "BASENETW HANDLE LOWER MSG Dest. Addr.= " << destNetwAddress << "\t Final Dest Addr = "<< finalDestNetwAddr << "\t\t my Netw Address = " << myNetwAddr << "\t Ttl value = " << remainingTtl <<"\n";;
	nextHop = finalDestNetwAddr;

	if(( finalDestNetwAddr>0)&(finalDestNetwAddr != myNetwAddr)&(m->getTtl()>0))
	{

		nextHop = m->getFinalDestAddr();
		char buff[255];

		// Is this node on our forwarding path to the destination node
		if((twoHop == true)) //&(forwardingTable(nextHop) >0 ))
		{

			while((index < numHosts)&(forwardTableHasDestAddress ==0))
			{
				coreEV << "\tindex" << index << "\toneHopDestTable[index] " << (networkAddressMapGlbl1[oneHopDestTableRT[index]]) << "\tnextHop " << nextHop <<"\n";
				if(oneHopDestTableRT[index] >=0)
				{
					if(networkAddressMapGlbl1[oneHopDestTableRT[index]] == nextHop)
					{
						forwardTableHasDestAddress++;
					}
				}
				index++;
			}

			if(forwardTableHasDestAddress>0)
			{
				//if(m->getTtl()>0)
				//{//Is this node on the forwarding path?
				ev << "BaseNetwLayer   : FORWARDING PACKET - ONE MORE HOP\n";
				numForwardedPackets++;
				sprintf(buff, "%d forwarded through %d to final destination %d", m->getSrcAddr(), destNetwAddress, finalDestNetwAddr);
				coreEV << buff << "\n\n";;
				getNode()->bubble(buff);

				if(m->getTtl() > 0)
				{
					forwardPacket(m, nextHop);
				}

			}
			else
			{
				ev << "DESTINATION NOT IN FORWARDING TABLE - DELETING PACKET\n";
				delete m;
			}

		}
		else //SINGLE HOP MODE
		{
			/*
			if(!(forwardingTable(nextHop) > 0))
			{
				ev << "NODE IS NOT ON OUR FORWARDING PATH - MESSAGE DELETED\n";
			}
			else
			{
				ev << "SINGLE HOP MODE - NO FORWARDING \n";
			}*/
			ev << "SINGLE HOP MODE - NO FORWARDING AND DESTINATION NOT REACHED\n";
			delete m;
		}
		//else delete m.!!!!
	}
	else
	{

		if((finalDestNetwAddr == myNetwAddr))
		{
			ev << "FINAL DESTINATION REACHED:  \n ===========================\n" ;
			if(m->getTtl()==maxTtl)
			{
				ev << " ONE HOP RECEIVED from " << m->getSrcAddr() << "\tto  " << myNetwAddr  << "\n ===========================\n" ;
				numberReceivedOneHop++;
			}
			else
			{
				ev << " TWO HOP RECEIVED from " << m->getSrcAddr() << "\tto  " << myNetwAddr << "\n ===========================\n"  ;
				numberReceivedTwoHop++;
			}
			sendUp(decapsMsg(m));				//delete m;

		}
		else //if ( finalDestNetwAddr != myNetwAddr )
		{

			if(finalDestNetwAddr == 0)
			{
				ev << "Address Corrupted Error deleting  \nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" ;
				delete m;
			}
			else if(finalDestNetwAddr == -1)
			{
				ev << "Broadcast Address - sending up  \nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" ;
				sendUp(decapsMsg(m));
			}
			else if (    (finalDestNetwAddr > 0)&(m->getTtl()< 1))
			{
				ev << "TTL == 0 & destination not reached, deleting packet  \nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" ;
				numberTtlZeroReceived++;
				delete m;
			}
			else
			{
				ev << "Error !!! , deleting packet (not transferred to application layer)  \n" ;
				ev << "FINAL DEST ADRESS = " << finalDestNetwAddr << "\t DEST ADRESS = " << destNetwAddress <<"\t MY ADRESS = " << myNetwAddr << "\n\n +++++++++++++++++++++\n";
				ev << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n";
				delete m;
			}
		}


	}

	//-----------------------------------------------
	// O R I G I N A L:

	//	coreEV << " handling packet from " << m->getSrcAddr() << endl;
	//	sendUp(decapsMsg(m));

	//------------------------------------------------


}

/**
 * Redefine this function if you want to process messages from upper
 * layers before they are send to lower layers.
 *
 * For the BaseNetwLayer we just use the destAddr of the network
 * message as a nextHop
 *
 * To forward the message to lower layers after processing it please
 * use @ref sendDown. It will take care of anything needed
 **/
void BaseNetwLayer_SRT_2::handleUpperMsg(cMessage* msg)
{
	assert(dynamic_cast<cPacket*>(msg));
	sendDown(encapsMsg(static_cast<cPacket*>(msg)));
}

/**
 * Redefine this function if you want to process control messages
 * from lower layers.
 *
 * This function currently handles one messagetype: TRANSMISSION_OVER.
 * If such a message is received in the network layer it is deleted.
 * This is done as this type of messages is passed on by the BaseMacLayer.
 *
 * It may be used by network protocols to determine when the lower layers
 * are finished sending a message.
 **/
void BaseNetwLayer_SRT_2::handleLowerControl(cMessage* msg)
{
	switch (msg->getKind())
	{
	case BaseMacLayer::TX_OVER:
		delete msg;
		break;
	default:
		EV << "BaseNetwLayer does not handle control messages called "
		<< msg->getName() << endl;
		delete msg;
	}
}

void BaseNetwLayer_SRT_2::finish()
{
	BaseLayer::finish();
	recordScalar("numForwardedPackets",numForwardedPackets);
	recordScalar("numberTransmittedPacketsOneHop",numberTransmittedPacketsOneHop);
	recordScalar("numberTransmittedPacketsTwoHops",numberTransmittedPacketsTwoHops);
	recordScalar("numberReceivedOneHop",numberReceivedOneHop);
	recordScalar("numberTtlZeroReceived",numberTtlZeroReceived);
	recordScalar("numberReceivedTwoHop",numberReceivedTwoHop);


}
